// This file is part of Eigen, a lightweight C++ template library
// for linear algebra.
//
// Copyright (C) 2016 Rasmus Munk Larsen <rmlarsen@google.com>
//
// This Source Code Form is subject to the terms of the Mozilla
// Public License v. 2.0. If a copy of the MPL was not distributed
// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.

#ifndef EIGEN_COMPLETEORTHOGONALDECOMPOSITION_H
#define EIGEN_COMPLETEORTHOGONALDECOMPOSITION_H

namespace Eigen {

namespace internal {
template<typename _MatrixType>
struct traits<CompleteOrthogonalDecomposition<_MatrixType>> : traits<_MatrixType>
{
	typedef MatrixXpr XprKind;
	typedef SolverStorage StorageKind;
	typedef int StorageIndex;
	enum
	{
		Flags = 0
	};
};

} // end namespace internal

/** \ingroup QR_Module
 *
 * \class CompleteOrthogonalDecomposition
 *
 * \brief Complete orthogonal decomposition (COD) of a matrix.
 *
 * \param MatrixType the type of the matrix of which we are computing the COD.
 *
 * This class performs a rank-revealing complete orthogonal decomposition of a
 * matrix  \b A into matrices \b P, \b Q, \b T, and \b Z such that
 * \f[
 *  \mathbf{A} \, \mathbf{P} = \mathbf{Q} \,
 *                     \begin{bmatrix} \mathbf{T} &  \mathbf{0} \\
 *                                     \mathbf{0} & \mathbf{0} \end{bmatrix} \, \mathbf{Z}
 * \f]
 * by using Householder transformations. Here, \b P is a permutation matrix,
 * \b Q and \b Z are unitary matrices and \b T an upper triangular matrix of
 * size rank-by-rank. \b A may be rank deficient.
 *
 * This class supports the \link InplaceDecomposition inplace decomposition \endlink mechanism.
 *
 * \sa MatrixBase::completeOrthogonalDecomposition()
 */
template<typename _MatrixType>
class CompleteOrthogonalDecomposition : public SolverBase<CompleteOrthogonalDecomposition<_MatrixType>>
{
  public:
	typedef _MatrixType MatrixType;
	typedef SolverBase<CompleteOrthogonalDecomposition> Base;

	template<typename Derived>
	friend struct internal::solve_assertion;

	EIGEN_GENERIC_PUBLIC_INTERFACE(CompleteOrthogonalDecomposition)
	enum
	{
		MaxRowsAtCompileTime = MatrixType::MaxRowsAtCompileTime,
		MaxColsAtCompileTime = MatrixType::MaxColsAtCompileTime
	};
	typedef typename internal::plain_diag_type<MatrixType>::type HCoeffsType;
	typedef PermutationMatrix<ColsAtCompileTime, MaxColsAtCompileTime> PermutationType;
	typedef typename internal::plain_row_type<MatrixType, Index>::type IntRowVectorType;
	typedef typename internal::plain_row_type<MatrixType>::type RowVectorType;
	typedef typename internal::plain_row_type<MatrixType, RealScalar>::type RealRowVectorType;
	typedef HouseholderSequence<MatrixType,
								typename internal::remove_all<typename HCoeffsType::ConjugateReturnType>::type>
		HouseholderSequenceType;
	typedef typename MatrixType::PlainObject PlainObject;

  private:
	typedef typename PermutationType::Index PermIndexType;

  public:
	/**
	 * \brief Default Constructor.
	 *
	 * The default constructor is useful in cases in which the user intends to
	 * perform decompositions via
	 * \c CompleteOrthogonalDecomposition::compute(const* MatrixType&).
	 */
	CompleteOrthogonalDecomposition()
		: m_cpqr()
		, m_zCoeffs()
		, m_temp()
	{
	}

	/** \brief Default Constructor with memory preallocation
	 *
	 * Like the default constructor but with preallocation of the internal data
	 * according to the specified problem \a size.
	 * \sa CompleteOrthogonalDecomposition()
	 */
	CompleteOrthogonalDecomposition(Index rows, Index cols)
		: m_cpqr(rows, cols)
		, m_zCoeffs((std::min)(rows, cols))
		, m_temp(cols)
	{
	}

	/** \brief Constructs a complete orthogonal decomposition from a given
	 * matrix.
	 *
	 * This constructor computes the complete orthogonal decomposition of the
	 * matrix \a matrix by calling the method compute(). The default
	 * threshold for rank determination will be used. It is a short cut for:
	 *
	 * \code
	 * CompleteOrthogonalDecomposition<MatrixType> cod(matrix.rows(),
	 *                                                 matrix.cols());
	 * cod.setThreshold(Default);
	 * cod.compute(matrix);
	 * \endcode
	 *
	 * \sa compute()
	 */
	template<typename InputType>
	explicit CompleteOrthogonalDecomposition(const EigenBase<InputType>& matrix)
		: m_cpqr(matrix.rows(), matrix.cols())
		, m_zCoeffs((std::min)(matrix.rows(), matrix.cols()))
		, m_temp(matrix.cols())
	{
		compute(matrix.derived());
	}

	/** \brief Constructs a complete orthogonal decomposition from a given matrix
	 *
	 * This overloaded constructor is provided for \link InplaceDecomposition inplace decomposition \endlink when \c
	 * MatrixType is a Eigen::Ref.
	 *
	 * \sa CompleteOrthogonalDecomposition(const EigenBase&)
	 */
	template<typename InputType>
	explicit CompleteOrthogonalDecomposition(EigenBase<InputType>& matrix)
		: m_cpqr(matrix.derived())
		, m_zCoeffs((std::min)(matrix.rows(), matrix.cols()))
		, m_temp(matrix.cols())
	{
		computeInPlace();
	}

#ifdef EIGEN_PARSED_BY_DOXYGEN
	/** This method computes the minimum-norm solution X to a least squares
	 * problem \f[\mathrm{minimize} \|A X - B\|, \f] where \b A is the matrix of
	 * which \c *this is the complete orthogonal decomposition.
	 *
	 * \param b the right-hand sides of the problem to solve.
	 *
	 * \returns a solution.
	 *
	 */
	template<typename Rhs>
	inline const Solve<CompleteOrthogonalDecomposition, Rhs> solve(const MatrixBase<Rhs>& b) const;
#endif

	HouseholderSequenceType householderQ(void) const;
	HouseholderSequenceType matrixQ(void) const { return m_cpqr.householderQ(); }

	/** \returns the matrix \b Z.
	 */
	MatrixType matrixZ() const
	{
		MatrixType Z = MatrixType::Identity(m_cpqr.cols(), m_cpqr.cols());
		applyZOnTheLeftInPlace<false>(Z);
		return Z;
	}

	/** \returns a reference to the matrix where the complete orthogonal
	 * decomposition is stored
	 */
	const MatrixType& matrixQTZ() const { return m_cpqr.matrixQR(); }

	/** \returns a reference to the matrix where the complete orthogonal
	 * decomposition is stored.
	 * \warning The strict lower part and \code cols() - rank() \endcode right
	 * columns of this matrix contains internal values.
	 * Only the upper triangular part should be referenced. To get it, use
	 * \code matrixT().template triangularView<Upper>() \endcode
	 * For rank-deficient matrices, use
	 * \code
	 * matrixR().topLeftCorner(rank(), rank()).template triangularView<Upper>()
	 * \endcode
	 */
	const MatrixType& matrixT() const { return m_cpqr.matrixQR(); }

	template<typename InputType>
	CompleteOrthogonalDecomposition& compute(const EigenBase<InputType>& matrix)
	{
		// Compute the column pivoted QR factorization A P = Q R.
		m_cpqr.compute(matrix);
		computeInPlace();
		return *this;
	}

	/** \returns a const reference to the column permutation matrix */
	const PermutationType& colsPermutation() const { return m_cpqr.colsPermutation(); }

	/** \returns the absolute value of the determinant of the matrix of which
	 * *this is the complete orthogonal decomposition. It has only linear
	 * complexity (that is, O(n) where n is the dimension of the square matrix)
	 * as the complete orthogonal decomposition has already been computed.
	 *
	 * \note This is only for square matrices.
	 *
	 * \warning a determinant can be very big or small, so for matrices
	 * of large enough dimension, there is a risk of overflow/underflow.
	 * One way to work around that is to use logAbsDeterminant() instead.
	 *
	 * \sa logAbsDeterminant(), MatrixBase::determinant()
	 */
	typename MatrixType::RealScalar absDeterminant() const;

	/** \returns the natural log of the absolute value of the determinant of the
	 * matrix of which *this is the complete orthogonal decomposition. It has
	 * only linear complexity (that is, O(n) where n is the dimension of the
	 * square matrix) as the complete orthogonal decomposition has already been
	 * computed.
	 *
	 * \note This is only for square matrices.
	 *
	 * \note This method is useful to work around the risk of overflow/underflow
	 * that's inherent to determinant computation.
	 *
	 * \sa absDeterminant(), MatrixBase::determinant()
	 */
	typename MatrixType::RealScalar logAbsDeterminant() const;

	/** \returns the rank of the matrix of which *this is the complete orthogonal
	 * decomposition.
	 *
	 * \note This method has to determine which pivots should be considered
	 * nonzero. For that, it uses the threshold value that you can control by
	 * calling setThreshold(const RealScalar&).
	 */
	inline Index rank() const { return m_cpqr.rank(); }

	/** \returns the dimension of the kernel of the matrix of which *this is the
	 * complete orthogonal decomposition.
	 *
	 * \note This method has to determine which pivots should be considered
	 * nonzero. For that, it uses the threshold value that you can control by
	 * calling setThreshold(const RealScalar&).
	 */
	inline Index dimensionOfKernel() const { return m_cpqr.dimensionOfKernel(); }

	/** \returns true if the matrix of which *this is the decomposition represents
	 * an injective linear map, i.e. has trivial kernel; false otherwise.
	 *
	 * \note This method has to determine which pivots should be considered
	 * nonzero. For that, it uses the threshold value that you can control by
	 * calling setThreshold(const RealScalar&).
	 */
	inline bool isInjective() const { return m_cpqr.isInjective(); }

	/** \returns true if the matrix of which *this is the decomposition represents
	 * a surjective linear map; false otherwise.
	 *
	 * \note This method has to determine which pivots should be considered
	 * nonzero. For that, it uses the threshold value that you can control by
	 * calling setThreshold(const RealScalar&).
	 */
	inline bool isSurjective() const { return m_cpqr.isSurjective(); }

	/** \returns true if the matrix of which *this is the complete orthogonal
	 * decomposition is invertible.
	 *
	 * \note This method has to determine which pivots should be considered
	 * nonzero. For that, it uses the threshold value that you can control by
	 * calling setThreshold(const RealScalar&).
	 */
	inline bool isInvertible() const { return m_cpqr.isInvertible(); }

	/** \returns the pseudo-inverse of the matrix of which *this is the complete
	 * orthogonal decomposition.
	 * \warning: Do not compute \c this->pseudoInverse()*rhs to solve a linear systems.
	 * It is more efficient and numerically stable to call \c this->solve(rhs).
	 */
	inline const Inverse<CompleteOrthogonalDecomposition> pseudoInverse() const
	{
		eigen_assert(m_cpqr.m_isInitialized && "CompleteOrthogonalDecomposition is not initialized.");
		return Inverse<CompleteOrthogonalDecomposition>(*this);
	}

	inline Index rows() const { return m_cpqr.rows(); }
	inline Index cols() const { return m_cpqr.cols(); }

	/** \returns a const reference to the vector of Householder coefficients used
	 * to represent the factor \c Q.
	 *
	 * For advanced uses only.
	 */
	inline const HCoeffsType& hCoeffs() const { return m_cpqr.hCoeffs(); }

	/** \returns a const reference to the vector of Householder coefficients
	 * used to represent the factor \c Z.
	 *
	 * For advanced uses only.
	 */
	const HCoeffsType& zCoeffs() const { return m_zCoeffs; }

	/** Allows to prescribe a threshold to be used by certain methods, such as
	 * rank(), who need to determine when pivots are to be considered nonzero.
	 * Most be called before calling compute().
	 *
	 * When it needs to get the threshold value, Eigen calls threshold(). By
	 * default, this uses a formula to automatically determine a reasonable
	 * threshold. Once you have called the present method
	 * setThreshold(const RealScalar&), your value is used instead.
	 *
	 * \param threshold The new value to use as the threshold.
	 *
	 * A pivot will be considered nonzero if its absolute value is strictly
	 * greater than
	 *  \f$ \vert pivot \vert \leqslant threshold \times \vert maxpivot \vert \f$
	 * where maxpivot is the biggest pivot.
	 *
	 * If you want to come back to the default behavior, call
	 * setThreshold(Default_t)
	 */
	CompleteOrthogonalDecomposition& setThreshold(const RealScalar& threshold)
	{
		m_cpqr.setThreshold(threshold);
		return *this;
	}

	/** Allows to come back to the default behavior, letting Eigen use its default
	 * formula for determining the threshold.
	 *
	 * You should pass the special object Eigen::Default as parameter here.
	 * \code qr.setThreshold(Eigen::Default); \endcode
	 *
	 * See the documentation of setThreshold(const RealScalar&).
	 */
	CompleteOrthogonalDecomposition& setThreshold(Default_t)
	{
		m_cpqr.setThreshold(Default);
		return *this;
	}

	/** Returns the threshold that will be used by certain methods such as rank().
	 *
	 * See the documentation of setThreshold(const RealScalar&).
	 */
	RealScalar threshold() const { return m_cpqr.threshold(); }

	/** \returns the number of nonzero pivots in the complete orthogonal
	 * decomposition. Here nonzero is meant in the exact sense, not in a
	 * fuzzy sense. So that notion isn't really intrinsically interesting,
	 * but it is still useful when implementing algorithms.
	 *
	 * \sa rank()
	 */
	inline Index nonzeroPivots() const { return m_cpqr.nonzeroPivots(); }

	/** \returns the absolute value of the biggest pivot, i.e. the biggest
	 *          diagonal coefficient of R.
	 */
	inline RealScalar maxPivot() const { return m_cpqr.maxPivot(); }

	/** \brief Reports whether the complete orthogonal decomposition was
	 * successful.
	 *
	 * \note This function always returns \c Success. It is provided for
	 * compatibility
	 * with other factorization routines.
	 * \returns \c Success
	 */
	ComputationInfo info() const
	{
		eigen_assert(m_cpqr.m_isInitialized && "Decomposition is not initialized.");
		return Success;
	}

#ifndef EIGEN_PARSED_BY_DOXYGEN
	template<typename RhsType, typename DstType>
	void _solve_impl(const RhsType& rhs, DstType& dst) const;

	template<bool Conjugate, typename RhsType, typename DstType>
	void _solve_impl_transposed(const RhsType& rhs, DstType& dst) const;
#endif

  protected:
	static void check_template_parameters() { EIGEN_STATIC_ASSERT_NON_INTEGER(Scalar); }

	template<bool Transpose_, typename Rhs>
	void _check_solve_assertion(const Rhs& b) const
	{
		EIGEN_ONLY_USED_FOR_DEBUG(b);
		eigen_assert(m_cpqr.m_isInitialized && "CompleteOrthogonalDecomposition is not initialized.");
		eigen_assert(
			(Transpose_ ? derived().cols() : derived().rows()) == b.rows() &&
			"CompleteOrthogonalDecomposition::solve(): invalid number of rows of the right hand side matrix b");
	}

	void computeInPlace();

	/** Overwrites \b rhs with \f$ \mathbf{Z} * \mathbf{rhs} \f$ or
	 *  \f$ \mathbf{\overline Z} * \mathbf{rhs} \f$ if \c Conjugate
	 *  is set to \c true.
	 */
	template<bool Conjugate, typename Rhs>
	void applyZOnTheLeftInPlace(Rhs& rhs) const;

	/** Overwrites \b rhs with \f$ \mathbf{Z}^* * \mathbf{rhs} \f$.
	 */
	template<typename Rhs>
	void applyZAdjointOnTheLeftInPlace(Rhs& rhs) const;

	ColPivHouseholderQR<MatrixType> m_cpqr;
	HCoeffsType m_zCoeffs;
	RowVectorType m_temp;
};

template<typename MatrixType>
typename MatrixType::RealScalar
CompleteOrthogonalDecomposition<MatrixType>::absDeterminant() const
{
	return m_cpqr.absDeterminant();
}

template<typename MatrixType>
typename MatrixType::RealScalar
CompleteOrthogonalDecomposition<MatrixType>::logAbsDeterminant() const
{
	return m_cpqr.logAbsDeterminant();
}

/** Performs the complete orthogonal decomposition of the given matrix \a
 * matrix. The result of the factorization is stored into \c *this, and a
 * reference to \c *this is returned.
 *
 * \sa class CompleteOrthogonalDecomposition,
 * CompleteOrthogonalDecomposition(const MatrixType&)
 */
template<typename MatrixType>
void
CompleteOrthogonalDecomposition<MatrixType>::computeInPlace()
{
	check_template_parameters();

	// the column permutation is stored as int indices, so just to be sure:
	eigen_assert(m_cpqr.cols() <= NumTraits<int>::highest());

	const Index rank = m_cpqr.rank();
	const Index cols = m_cpqr.cols();
	const Index rows = m_cpqr.rows();
	m_zCoeffs.resize((std::min)(rows, cols));
	m_temp.resize(cols);

	if (rank < cols) {
		// We have reduced the (permuted) matrix to the form
		//   [R11 R12]
		//   [ 0  R22]
		// where R11 is r-by-r (r = rank) upper triangular, R12 is
		// r-by-(n-r), and R22 is empty or the norm of R22 is negligible.
		// We now compute the complete orthogonal decomposition by applying
		// Householder transformations from the right to the upper trapezoidal
		// matrix X = [R11 R12] to zero out R12 and obtain the factorization
		// [R11 R12] = [T11 0] * Z, where T11 is r-by-r upper triangular and
		// Z = Z(0) * Z(1) ... Z(r-1) is an n-by-n orthogonal matrix.
		// We store the data representing Z in R12 and m_zCoeffs.
		for (Index k = rank - 1; k >= 0; --k) {
			if (k != rank - 1) {
				// Given the API for Householder reflectors, it is more convenient if
				// we swap the leading parts of columns k and r-1 (zero-based) to form
				// the matrix X_k = [X(0:k, k), X(0:k, r:n)]
				m_cpqr.m_qr.col(k).head(k + 1).swap(m_cpqr.m_qr.col(rank - 1).head(k + 1));
			}
			// Construct Householder reflector Z(k) to zero out the last row of X_k,
			// i.e. choose Z(k) such that
			// [X(k, k), X(k, r:n)] * Z(k) = [beta, 0, .., 0].
			RealScalar beta;
			m_cpqr.m_qr.row(k).tail(cols - rank + 1).makeHouseholderInPlace(m_zCoeffs(k), beta);
			m_cpqr.m_qr(k, rank - 1) = beta;
			if (k > 0) {
				// Apply Z(k) to the first k rows of X_k
				m_cpqr.m_qr.topRightCorner(k, cols - rank + 1)
					.applyHouseholderOnTheRight(
						m_cpqr.m_qr.row(k).tail(cols - rank).adjoint(), m_zCoeffs(k), &m_temp(0));
			}
			if (k != rank - 1) {
				// Swap X(0:k,k) back to its proper location.
				m_cpqr.m_qr.col(k).head(k + 1).swap(m_cpqr.m_qr.col(rank - 1).head(k + 1));
			}
		}
	}
}

template<typename MatrixType>
template<bool Conjugate, typename Rhs>
void
CompleteOrthogonalDecomposition<MatrixType>::applyZOnTheLeftInPlace(Rhs& rhs) const
{
	const Index cols = this->cols();
	const Index nrhs = rhs.cols();
	const Index rank = this->rank();
	Matrix<typename Rhs::Scalar, Dynamic, 1> temp((std::max)(cols, nrhs));
	for (Index k = rank - 1; k >= 0; --k) {
		if (k != rank - 1) {
			rhs.row(k).swap(rhs.row(rank - 1));
		}
		rhs.middleRows(rank - 1, cols - rank + 1)
			.applyHouseholderOnTheLeft(
				matrixQTZ().row(k).tail(cols - rank).transpose().template conjugateIf<!Conjugate>(),
				zCoeffs().template conjugateIf<Conjugate>()(k),
				&temp(0));
		if (k != rank - 1) {
			rhs.row(k).swap(rhs.row(rank - 1));
		}
	}
}

template<typename MatrixType>
template<typename Rhs>
void
CompleteOrthogonalDecomposition<MatrixType>::applyZAdjointOnTheLeftInPlace(Rhs& rhs) const
{
	const Index cols = this->cols();
	const Index nrhs = rhs.cols();
	const Index rank = this->rank();
	Matrix<typename Rhs::Scalar, Dynamic, 1> temp((std::max)(cols, nrhs));
	for (Index k = 0; k < rank; ++k) {
		if (k != rank - 1) {
			rhs.row(k).swap(rhs.row(rank - 1));
		}
		rhs.middleRows(rank - 1, cols - rank + 1)
			.applyHouseholderOnTheLeft(matrixQTZ().row(k).tail(cols - rank).adjoint(), zCoeffs()(k), &temp(0));
		if (k != rank - 1) {
			rhs.row(k).swap(rhs.row(rank - 1));
		}
	}
}

#ifndef EIGEN_PARSED_BY_DOXYGEN
template<typename _MatrixType>
template<typename RhsType, typename DstType>
void
CompleteOrthogonalDecomposition<_MatrixType>::_solve_impl(const RhsType& rhs, DstType& dst) const
{
	const Index rank = this->rank();
	if (rank == 0) {
		dst.setZero();
		return;
	}

	// Compute c = Q^* * rhs
	typename RhsType::PlainObject c(rhs);
	c.applyOnTheLeft(matrixQ().setLength(rank).adjoint());

	// Solve T z = c(1:rank, :)
	dst.topRows(rank) = matrixT().topLeftCorner(rank, rank).template triangularView<Upper>().solve(c.topRows(rank));

	const Index cols = this->cols();
	if (rank < cols) {
		// Compute y = Z^* * [ z ]
		//                   [ 0 ]
		dst.bottomRows(cols - rank).setZero();
		applyZAdjointOnTheLeftInPlace(dst);
	}

	// Undo permutation to get x = P^{-1} * y.
	dst = colsPermutation() * dst;
}

template<typename _MatrixType>
template<bool Conjugate, typename RhsType, typename DstType>
void
CompleteOrthogonalDecomposition<_MatrixType>::_solve_impl_transposed(const RhsType& rhs, DstType& dst) const
{
	const Index rank = this->rank();

	if (rank == 0) {
		dst.setZero();
		return;
	}

	typename RhsType::PlainObject c(colsPermutation().transpose() * rhs);

	if (rank < cols()) {
		applyZOnTheLeftInPlace<!Conjugate>(c);
	}

	matrixT()
		.topLeftCorner(rank, rank)
		.template triangularView<Upper>()
		.transpose()
		.template conjugateIf<Conjugate>()
		.solveInPlace(c.topRows(rank));

	dst.topRows(rank) = c.topRows(rank);
	dst.bottomRows(rows() - rank).setZero();

	dst.applyOnTheLeft(householderQ().setLength(rank).template conjugateIf<!Conjugate>());
}
#endif

namespace internal {

template<typename MatrixType>
struct traits<Inverse<CompleteOrthogonalDecomposition<MatrixType>>>
	: traits<typename Transpose<typename MatrixType::PlainObject>::PlainObject>
{
	enum
	{
		Flags = 0
	};
};

template<typename DstXprType, typename MatrixType>
struct Assignment<
	DstXprType,
	Inverse<CompleteOrthogonalDecomposition<MatrixType>>,
	internal::assign_op<typename DstXprType::Scalar, typename CompleteOrthogonalDecomposition<MatrixType>::Scalar>,
	Dense2Dense>
{
	typedef CompleteOrthogonalDecomposition<MatrixType> CodType;
	typedef Inverse<CodType> SrcXprType;
	static void run(DstXprType& dst,
					const SrcXprType& src,
					const internal::assign_op<typename DstXprType::Scalar, typename CodType::Scalar>&)
	{
		typedef Matrix<typename CodType::Scalar,
					   CodType::RowsAtCompileTime,
					   CodType::RowsAtCompileTime,
					   0,
					   CodType::MaxRowsAtCompileTime,
					   CodType::MaxRowsAtCompileTime>
			IdentityMatrixType;
		dst = src.nestedExpression().solve(IdentityMatrixType::Identity(src.cols(), src.cols()));
	}
};

} // end namespace internal

/** \returns the matrix Q as a sequence of householder transformations */
template<typename MatrixType>
typename CompleteOrthogonalDecomposition<MatrixType>::HouseholderSequenceType
CompleteOrthogonalDecomposition<MatrixType>::householderQ() const
{
	return m_cpqr.householderQ();
}

/** \return the complete orthogonal decomposition of \c *this.
 *
 * \sa class CompleteOrthogonalDecomposition
 */
template<typename Derived>
const CompleteOrthogonalDecomposition<typename MatrixBase<Derived>::PlainObject>
MatrixBase<Derived>::completeOrthogonalDecomposition() const
{
	return CompleteOrthogonalDecomposition<PlainObject>(eval());
}

} // end namespace Eigen

#endif // EIGEN_COMPLETEORTHOGONALDECOMPOSITION_H
